home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / Composer.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  13.9 KB  |  594 lines

  1.  
  2. #import <appkit/appkit.h>
  3. #import <misckit/misckit.h>
  4. #import <misckit/MiscDateView.h>
  5.  
  6. #import "Alexandra.h"
  7. #import "Composer.h"
  8. #import "NNTP.h"
  9. #import "descriptors.h"
  10. #import "ArticleViewControl.h"
  11. #import "ArticleSet.h"
  12. #import "NntpPanelControl.h"
  13. #import "plain-subject.h"
  14. #include <ctype.h>
  15. #include <dbkit/DBTableView.h>
  16.  
  17. #define    OK_POSTED    240
  18.  
  19. static MiscList    *myComposers;
  20. static XTDispatchAction *emacs_action;
  21. static XTDispatchAction *none_action;
  22.  
  23. @implementation Composer
  24.  
  25. //-----------------------------------------------------------
  26. // Klassen Methoden, Verwalten der Composer Liste 
  27. //-----------------------------------------------------------
  28.  
  29. + initialize
  30. {
  31.     char buf[MAXPATHLEN + 1];
  32.     const char *userBindings;
  33.     
  34.     if(self==[Composer class])
  35.     {
  36.         myComposers=[[MiscList alloc] init];
  37.         emacs_action=[[XTDispatchAction alloc] init];
  38.         none_action=[[XTDispatchAction alloc] init];
  39.         
  40.         if([[NXBundle mainBundle] getPath:buf forResource:"emacs" ofType:"keys"])
  41.             [emacs_action loadFromFile:buf estream:nil];
  42.         
  43.         if(userBindings=[NXApp defaultValue:"KeyString"]){
  44.             [emacs_action addBindings:userBindings estream:nil];
  45.             [none_action addBindings:userBindings estream:nil];
  46.         }
  47.     }
  48.     return self;
  49. }
  50.  
  51.  
  52. + newWindowForServer:(NNTP *)server;
  53.     {
  54.     Composer    *new;
  55.     
  56.     new=[[self alloc] initForServer:server];
  57.     [myComposers addObject:new];
  58.     return new;
  59.     }    
  60.     
  61.  
  62. + removeComposer:(Composer *)composer;
  63.     {
  64.     [myComposers removeObject:composer];
  65.     return self;
  66.     }
  67.     
  68.  
  69. + killComposerForServer:(NNTP *)server;
  70.     {
  71.     Composer *c;
  72.     
  73.     for(c=[myComposers setLastObject];c;c=[myComposers setPreviousObject])
  74.         if([c nntpServer]==server)
  75.             [c free];
  76.     return self;
  77.     }
  78.  
  79. + confirmTermination
  80. {
  81.     int r1,r2;
  82.    int j=[myComposers count];
  83.     if(j>0)
  84.         do{
  85.             r1=NXRunAlertPanel("ALEXANDRA","There are unsend messages.","Review unsend","Quit anyway","Cancel");
  86.             if(r1==NX_ALERTOTHER)
  87.                 return nil;
  88.             else if(r1==NX_ALERTALTERNATE){
  89.                 int i;
  90.                 for(i=j-1;i>=0;i--)
  91.                     [[myComposers objectAt:i] free];
  92.                 
  93.                 return self;
  94.             }
  95.             else{
  96.                 int i;
  97.                 BOOL canceled=FALSE;
  98.                 for(i=j-1;i>=0;i--){
  99.                     id aComposer=[myComposers objectAt:i];
  100.                     [[aComposer window] makeKeyAndOrderFront:self];
  101.                     r2=NXRunAlertPanel("ALEXANDRA","Send this message?","Send","Don't send","Cancel");
  102.                     if(r2==NX_ALERTOTHER){
  103.                         canceled=TRUE;
  104.                         break;
  105.                     }
  106.                     else if(r2==NX_ALERTDEFAULT)
  107.                         [aComposer post:self];
  108.                     else
  109.                         [aComposer free];
  110.                 }
  111.                 if(canceled)
  112.                     j=[myComposers count];
  113.                 else
  114.                     return self;
  115.             }
  116.         } while(j>0);
  117.     
  118.     return self;
  119. }            
  120. //-----------------------------------------------------------
  121. // INIT & FREE 
  122. //-----------------------------------------------------------
  123.  
  124. - initForServer:(NNTP *)theServer;
  125.     {
  126.     float char78Width;
  127.     NXRect textFrame;
  128.     NXRect windowFrame;
  129.     NXAtom    ids[]={[KVPair keyIdentifier],[KVPair valueIdentifier],NULL};
  130.     
  131.     if([NXApp loadNibSection:"Composer.nib" owner:self withNames:NO]==nil)
  132.         EM_ERROR(EGENFileNotFound,"Composer.nib",NULL);
  133.     [self setNntpServer:theServer];
  134.     headerController=[[[MiscTableController alloc] init] setTableView:headerTable withIdentifiers:ids];
  135.     [self getDefaults];
  136.  
  137.     char78Width=78*[[theText font] getWidthOf:"A"];
  138.     [theText getFrame:&textFrame];
  139.     [[theText window] getFrame:&windowFrame];
  140.     char78Width+=NX_WIDTH(&windowFrame)-NX_WIDTH(&textFrame);
  141.  
  142.     [[theText window] sizeWindow:char78Width :NX_HEIGHT(&windowFrame)];
  143.  
  144.    if([NXApp defaultBoolValue:DEFAULT_APPEND_SIG]){
  145.       NXStream *signatureFile;
  146.       const char *home=NXHomeDirectory();
  147.       char *filename;
  148.  
  149.       filename=(char *)NXZoneCalloc(MYZONE,(strlen(home)+12),sizeof(char));
  150.       sprintf(filename,"%s/.signature",home);
  151.  
  152.       if((signatureFile=NXMapFile(filename,NX_READONLY))!=NULL){
  153.           [theText readText:signatureFile];
  154.         [theText setSel:0:0];
  155.           [theText replaceSel:"\n-- \n"];
  156.           [theText setSel:0:0];
  157.         NXClose(signatureFile);
  158.       }
  159.         else
  160.             NXRunAlertPanel("Alexandra","Cannot append signature."
  161.                     " File `~/.signature' not found.","Cancel",NULL,NULL);
  162.       NXZoneFree(MYZONE,filename);
  163.    }
  164.  
  165.  
  166.     [[theText window] makeKeyAndOrderFront:self];
  167.     
  168.     return self;
  169.     }
  170.  
  171.  
  172.  
  173. - getDefaults
  174.     {
  175.     KVPair        *emptypair=[[KVPair allocFromZone:MYZONE] init];
  176.     char        defname[200];
  177.     const char    *d;
  178.     int            i,n=[NXApp defaultIntValue:DEFAULT_XHEADER_COUNT];
  179.  
  180.     [headerController empty:nil];
  181.     for(i=0;i<n;i++)
  182.         {
  183.         sprintf(defname,DEFAULT_XHEADERS,i+1);
  184.         d=[NXApp defaultValue:defname];
  185.         if(d)
  186.             [headerController addRow:[[KVPair alloc] init:d delimiter:':']];
  187.         else
  188.             [headerController addRow:emptypair];
  189.         }
  190.     [emptypair free];
  191.        return self;
  192.     }
  193.   
  194.   
  195. - free
  196. {
  197.    [[self class] removeComposer:self];
  198.    [[[headerTable window] close] free];
  199.    [[window close] free];
  200.  
  201.    if(subject!=NULL)
  202.       free(subject);
  203.    if(newsgroups!=NULL)
  204.       free(newsgroups);
  205.    if(references!=NULL)
  206.       free(references);
  207.    if(theQuotingStream!=NULL)
  208.       NXCloseMemory(theQuotingStream,NX_FREEBUFFER);
  209.    return [super free];
  210. }
  211.  
  212.  
  213. - awakeFromNib
  214.     {
  215.     
  216.     if(strcmp([NXApp defaultValue:DEFAULT_KEY_BASE],"emacs")==0)
  217.         local_action=emacs_action;
  218.     else
  219.         local_action=none_action;
  220.         
  221.     theText=[theText docView];
  222.     [theText setMonoFont:YES];
  223.     [theText setFont:    [Font userFixedPitchFontOfSize:0 
  224.                             matrix:NX_FLIPPEDMATRIX]];
  225.         
  226.     [theText setInitialAction:local_action];
  227.     [headerForm setNextText:theText];
  228.     return self;
  229.     }
  230.  
  231.  
  232. //-----------------------------------------------------------
  233. // quoting 
  234. //-----------------------------------------------------------
  235.  
  236. - followup:sender
  237. {
  238.    char *newSubject,*oldSubject;
  239.    BOOL wasReply;
  240.  
  241.    if(subject==NULL)
  242.       return self;
  243.    oldSubject=plain_subject(subject,&wasReply);
  244.    newSubject=(char *)NXZoneCalloc(MYZONE,(strlen(subject)+5),sizeof(char));
  245.    sprintf(newSubject,"Re: %s",oldSubject);
  246.    [subjectCell setStringValueNoCopy:newSubject shouldFree:YES];
  247.  
  248.    [newsgroupCell setStringValue:newsgroups];
  249.    [followupButton setIcon:[followupButton altIcon]];
  250.    [followupButton setTitle:[followupButton altTitle]];
  251.    [followupButton setAction:@selector(includeArticle:)];
  252.    if(strlen(references)>0)
  253.       insert_refs=TRUE;
  254.    return self;
  255. }
  256.  
  257. - includeArticle:sender
  258. {
  259.     char *buf;
  260.     int len,maxlen;
  261.     
  262.     if(theQuotingStream!=NULL){
  263.           NXGetMemoryBuffer(theQuotingStream,&buf,&len,&maxlen);
  264.        [theText setSel:0:0];
  265.        [theText replaceSel:buf length:len];
  266.     }
  267.     [followupButton setEnabled:FALSE];
  268.     return self;
  269. }
  270.  
  271. - post:sender
  272. {
  273.    char month_name[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep",
  274.                         "Oct","Nov","Dec"};
  275.    char mime_header[]="MIME-Version: 1.0\nContent-Type: text/plain; charset=iso-8859-1\nContent-Transfer-Encoding: 8bit\n";
  276.    char *a2String;
  277.    NXStream *theStream;
  278.    long bodyPos;
  279.    int textLength;
  280.    int line;
  281.    int pos;
  282.    NXBreakArray    *theBreaks=0;
  283.    int nbreaks;
  284.    NXLineDesc *breaks;
  285.    BOOL asciiBody;
  286.    BOOL userDefdFrom=NO;
  287.    int statusCode;
  288.    MiscList *headers;
  289.    KVPair    *pair;
  290.  
  291.    if([newsgroupCell stringValue][0]=='\0'){
  292.       NXRunAlertPanel("ALEXANDRA","No Newsgroups header field body. Can't post.",NULL,NULL,NULL);
  293.       return nil;
  294.    }
  295.    if([subjectCell stringValue][0]=='\0'){
  296.       NXRunAlertPanel("ALEXANDRA","No Subject header field body. Can't post.",NULL,NULL,NULL);
  297.       return nil;
  298.    }
  299.    if([theText textLength]==0){
  300.       NXRunAlertPanel("ALEXANDRA","No article body. Can't post.",NULL,NULL,NULL);
  301.       return nil;
  302.    }
  303.    if([expireButton state] && ([dateView isDateValid:self]==FALSE))
  304.       return nil;
  305.     
  306.     [headerTable endEditing];
  307.     
  308.    theStream=NXOpenMemory(NULL,0,NX_READWRITE);
  309.    NXSeek(theStream,0,NX_FROMSTART);
  310.  
  311.    //Write MIME-Header
  312.    asciiBody=[self asciiBody];
  313.    if(asciiBody==FALSE)
  314.       NXPrintf(theStream,"%s",mime_header);
  315.    
  316.    // Write Newsgroups: header
  317.    NXPrintf(theStream,"Newsgroups: %s\n",[newsgroupCell stringValue]);
  318.    
  319.    // Write Subject: header
  320.    NXPrintf(theStream,"Subject: %s\n",[subjectCell stringValue]);
  321.  
  322.    // Write References: header
  323.    if(insert_refs==TRUE)
  324.       NXPrintf(theStream,"References: %s\n",references);
  325.  
  326.     headers=[headerController rows];
  327.     for(pair=[headers setFirstObject]; pair; pair=[headers setNextObject])
  328.         if([pair hasValue])
  329.             {
  330.             if(!strcasecmp([pair key],"from"))
  331.                 userDefdFrom=YES;
  332.             NXPrintf(theStream,"%s: %s\n",[pair key],[pair value]);
  333.             }
  334.  
  335.    // Write Expires: header
  336.    if([expireButton state]){
  337.       int day=[dateView getDay:self];
  338.       const char *month=month_name[[dateView getMonth:self]-1];
  339.       int year=[dateView getYear:self];
  340.  
  341.       NXPrintf(theStream,"Expires: %d %s %d 00:00:00 GMT\n",day,month,year);
  342.    }
  343.  
  344.    if(!userDefdFrom){
  345.         a2String=[[FromHeaderController new] validFromAddress];
  346.         if(!a2String)
  347.             {
  348.               NXCloseMemory(theStream,NX_FREEBUFFER);
  349.             return nil;
  350.             }
  351.         NXPrintf(theStream,"From: %s\n",a2String);
  352.         free(a2String);
  353.    }
  354.    NXPrintf(theStream,"X-Newsreader: %s.app (%s)\n",
  355.                            [NXApp appName],[NXApp appVersion]);
  356.  
  357.    //Write end of header
  358.    NXPutc(theStream,'\n');
  359.    bodyPos=NXTell(theStream);
  360.    
  361.    /*write body*/
  362.    textLength=[theText textLength];
  363.    object_getInstanceVariable(theText, "theBreaks", (void **)&theBreaks);
  364.    nbreaks = theBreaks->chunk.used/sizeof(NXLineDesc);
  365.    breaks = theBreaks->breaks;
  366.  
  367.    for(pos=0,line=0;line<nbreaks && pos<textLength;line++) {
  368.       int lineChange;
  369.       int endParagraph;
  370.       int len,nlen;
  371.       char *buf,*realbuf;
  372.  
  373.       lineChange = breaks[line] & 0x8000;
  374.       endParagraph = breaks[line] & 0x4000;
  375.         
  376.       len = breaks[line] & 0x3fff;
  377.       buf = malloc(len+2);
  378.       realbuf=buf;
  379.       buf++;
  380.       nlen = [theText getSubstring:buf start:pos length:len];
  381.       
  382.       if(buf[0]=='.'){
  383.          buf--;
  384.          nlen++;
  385.          buf[0]='.';
  386.       }
  387.  
  388.       if((nlen>0)&&(buf[nlen-1]!='\n')){
  389.          if(buf[nlen-1]=='\0')
  390.             buf[nlen-1]='\n';
  391.          else{
  392.             buf[nlen] = '\n';
  393.             nlen++;
  394.          }
  395.       }
  396.  
  397.       NXWrite(theStream,buf,nlen);    
  398.       free(realbuf);
  399.       pos+=len;
  400.       if (lineChange!=0) {
  401.          /* "if the line change bit is set, the descriptor is
  402.           * the first field of a NXHeightChange...." */
  403.          line += sizeof(NXHeightInfo) / sizeof(NXLineDesc);
  404.       }
  405.    }
  406.  
  407.    statusCode=[nntpServer postArticle:theStream];
  408.    if(statusCode==OK_POSTED){
  409.       [window close];
  410.       NXCloseMemory(theStream,NX_FREEBUFFER);
  411.       [self free];
  412.         return self;
  413.    }
  414.     
  415.    return nil;
  416. }
  417.  
  418. - preparePostTo:(List *)newsgroupList selArticle:(Article *)anArticle 
  419.     inView:(ArticleViewControl *)controlView;
  420. {
  421.    int i,j,strlength;
  422.    char *newsgroup_str;
  423.    char *ptr;
  424.    const char *newsgroup;
  425.    char *msg_id,*refs;
  426.  
  427.    //Make newsgrouplist string and set Newsgroups: field
  428.    strlength=0;
  429.    j=[newsgroupList count];
  430.    for(i=0;i<j;i++)
  431.       strlength += strlen([[newsgroupList objectAt:i] stringValue]);
  432.    newsgroup_str=(char *)malloc((strlength+j)*sizeof(char));
  433.    ptr=newsgroup_str;
  434.    for(i=0;i<j;i++){
  435.       newsgroup=[[newsgroupList objectAt:i] stringValue];
  436.       strcpy(ptr,newsgroup);
  437.       ptr+=strlen(newsgroup);
  438.       if(i<j-1)
  439.          *ptr=',';
  440.       ptr++;
  441.    }
  442.    [newsgroupCell setStringValueNoCopy:newsgroup_str shouldFree:YES];
  443.  
  444.    //remember selected article fields
  445.    subject=NULL;
  446.    if(anArticle==nil)
  447.       [followupButton setEnabled:FALSE];
  448.    else{
  449.       subject=NXCopyStringBuffer([anArticle header]->fieldBody[SUBJECT]);
  450.       
  451.       //where to followup?
  452.       if([anArticle header]->fieldBody[FOLLOWUP_TO]!=NULL)
  453.          newsgroups=NXCopyStringBuffer([anArticle header]->fieldBody[FOLLOWUP_TO]);
  454.       else
  455.          if([anArticle header]->fieldBody[NEWSGROUPS]!=NULL)
  456.             newsgroups=NXCopyStringBuffer([anArticle header]->fieldBody[NEWSGROUPS]);
  457.       
  458.       //References field
  459.       i=0; j=0;
  460.       refs=[anArticle header]->fieldBody[REFS];
  461.       msg_id=[anArticle header]->fieldBody[MSG_ID];
  462.       if(refs!=NULL)
  463.          i=strlen(refs);
  464.       if(msg_id!=NULL)
  465.          j=strlen(msg_id);
  466.       references=(char *)malloc((i+j+3)*sizeof(char));
  467.       references[0]='\0';
  468.       if(refs!=NULL)
  469.          strcpy(references,refs);
  470.       if(msg_id!=NULL){
  471.          if(strlen(references)>0)
  472.             strcat(references," ");
  473.          strcat(references,msg_id);
  474.       }
  475.       insert_refs=FALSE;
  476.  
  477.       // Copy article body 
  478.       theQuotingStream=NXOpenMemory(NULL,0,NX_READWRITE);
  479.       if([anArticle header]->fieldBody[FROM]!=NULL)
  480.          NXPrintf(theQuotingStream,"%s wrote:\n",[anArticle header]->fieldBody[FROM]);
  481.       [controlView writeQuotedText:theQuotingStream];
  482.    }
  483.    //set DateViews date as today
  484.    [dateView getTodaysDate:self];
  485.  
  486.    return self;
  487. }
  488.  
  489. - window
  490. {
  491.    return window;
  492. }
  493.  
  494. - setNntpServer:(NNTP *)nntp
  495. {
  496.    nntpServer=nntp;
  497.    return self;
  498. }
  499.  
  500.  
  501. - (NNTP *)nntpServer;
  502. {
  503.    return nntpServer;
  504. }
  505.  
  506.  
  507. - printText:sender
  508. {
  509.    if([theText textLength]!=0){
  510.         [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
  511.       [theText printPSCode:self];
  512.     }
  513.     
  514.    return self;
  515. }
  516.  
  517.  
  518. - (BOOL)asciiBody
  519.     {
  520.     NXStream    *textStream;
  521.     int            len,maxlen;
  522.     char        *p;
  523.     
  524.     textStream= NXOpenMemory(NULL,0,NX_WRITEONLY);
  525.     [theText writeText:textStream];
  526.     NXGetMemoryBuffer(textStream,&p,&len,&maxlen);
  527.     for(;len>0;p++,len--)
  528.         if(!NXIsAscii(*p))
  529.             break;
  530.     NXCloseMemory(textStream,NX_FREEBUFFER);
  531.     return (len<=0);
  532.     }
  533.     
  534.  
  535. - theText
  536. {
  537.    return theText;
  538. }
  539.  
  540.  
  541. - windowWillClose:sender
  542. {
  543.    if([theText textLength]!=0){
  544.       int answer=NXRunAlertPanel("ALEXANDRA","Message was not posted.","Close Anyway", "Cancel",NULL);
  545.    if(answer==NX_ALERTALTERNATE)
  546.       return nil;
  547.    }
  548.    return [self free];
  549. }
  550.  
  551.  
  552. - windowWillMiniaturize:sender toMiniwindow:miniwindow
  553. {
  554.    [sender setMiniwindowIcon:"respond"];
  555.    return self;
  556. }
  557.  
  558.  
  559. - windowWillReturnFieldEditor:sender toObject:client
  560. {
  561.     return [XText newFieldEditorFor:sender
  562.                 initialAction:local_action
  563.                 estream:nil];
  564. }
  565.  
  566. - rot13:sender
  567. {
  568.     char *buf;
  569.     int i,j=[theText textLength];
  570.     
  571.     buf=malloc((j+2)*sizeof(char));
  572.     [theText getSubstring:buf start:0 length:j];
  573.     buf[j]='\0';
  574.     
  575.     for(i=0;i<j;i++) {
  576.        if(islower(buf[i]))
  577.             buf[i]=(((buf[i]-'a')+13)%26)+'a';
  578.       else if(isupper(buf[i]))
  579.               buf[i]=(((buf[i]-'A')+13)%26)+'A';
  580.    }
  581.  
  582.     [theText setText:buf];
  583.     
  584.     free(buf);
  585.     
  586.     return self;
  587. }
  588.  
  589.  
  590.  
  591.  
  592.  
  593. @end
  594.